<template>
  <div :id="state.id"></div>
</template>

<script lang="ts" setup>
  import {
    withDefaults,
    defineEmits,
    defineProps,
    onMounted,
    watch,
    reactive,
    onActivated,
    onDeactivated,
    onBeforeUnmount,
  } from 'vue';
  import { Message } from '@arco-design/web-vue';
  import { apiUpload } from '@/api/common/index';
  import {
    uuid,
    getTinymce,
    getContent,
    setContent,
    resetContent,
    setModeDisabled,
  } from './utils';

  interface Props {
    modelValue: string;
    config?: Record<string, any>;
    disabled?: boolean;
  }
  const props = withDefaults(defineProps<Props>(), {
    disabled: false,
  });
  const emit = defineEmits(['update:modelValue', 'init', 'change']);

  let mounting = true;

  const state = reactive<any>({
    editor: null,
    id: uuid('tinymce'),
    err: '',
  });

  const getModelValue = () => String(props.modelValue ?? '');

  const updateValue = (val) => emit('update:modelValue', val);

  const onChanged = (e, editor: any) => {
    if (!editor) editor = state.editor;

    const content = getContent(editor);

    updateValue(content);
    emit('change', content);
  };
  const onInited = (editor: any) => {
    state.editor = editor;

    setContent(getModelValue(), editor);

    if (props.disabled && editor.mode.get() !== 'readonly') {
      setModeDisabled(editor);
    }

    // change input undo redo keyup
    editor.on('change input undo redo', (e) => {
      onChanged(e, editor);
    });

    emit('init', editor);
  };

  const initEditor = () => {
    if (getTinymce() === null) {
      state.err = 'tinymce is null';
      return;
    }

    const setting = {
      selector: `#${state.id}`,
      language: 'zh_CN',
      file_picker_types: 'image media',
      video_template_callback: (data: any) => {
        return `<video src="${data.source}"
            type="${data.sourcemime}" controls width="${data.width}" height="${data.height}">`;
      },
      paste_as_text: true,
      paste_data_images: false,
      promotion: false,
      plugins:
        'importcss searchreplace autolink directionality visualblocks visualchars image link media table charmap insertdatetime advlist lists wordcount charmap',
      menubar: 'edit insert format table',
      toolbar:
        'undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | align numlist bullist | link image | table media | lineheight outdent indent| forecolor backcolor | charmap',
      file_picker_callback(callback: any, value: any, meta: { filetype: any }) {
        // debugger;
        // 文件分类
        let filetype = '.jpg, .jpeg, .png, .gif, .mp3, .mp4';
        // 后端接收上传文件的地址
        // 为不同插件指定文件类型及后端地址
        switch (meta.filetype) {
          case 'image':
            filetype = '.jpg, .jpeg, .png, .gif, .webp';
            break;
          case 'media':
            filetype = '.mp3, .mp4';
            break;
          case 'file':
          default:
        }

        // 模拟出一个input用于添加本地文件
        const input = document.createElement('input');
        input.setAttribute('type', 'file');
        input.setAttribute('accept', filetype);
        input.click();
        input.onchange = function () {
          // @ts-ignore
          const file = this.files[0];
          const formData = new FormData();
          formData.append('file', file, file.name);

          const messageId = Message.loading('正在上传...');
          apiUpload(formData)
            .then((json) => {
              callback(json.data.path);
            })
            .catch(() => {
              messageId.close();
            });
        };
      },

      height: 600,
      link_default_target: '_blank',
      quickbars_selection_toolbar:
        'bold italic quicklink h2 h3 blockquote quicktable',
      toolbar_mode: 'sliding',
      contextmenu: false,
      branding: false,
      ...(props?.config || {}),
      setup: (editor: any) => {
        editor.on('init', () => onInited(editor));
      },
    };

    getTinymce().init(setting);
    mounting = false;
  };

  watch(
    () => props.modelValue,
    (val, oldVal) => {
      if (!state.editor || !state.editor.initialized) return;
      if (oldVal === val || val === getContent(state.editor)) return;

      // eslint-disable-next-line consistent-return
      if (val === null) return resetContent('', state.editor);
      setContent(getModelValue(), state.editor);
    }
  );

  watch(
    () => props.disabled,
    (val) => {
      if (!state.editor || !state.editor.initialized) return;
      setModeDisabled(state.editor, val);
    }
  );

  defineExpose({
    id: state.id,
    editor: state.editor,
  });

  onMounted(() => {
    if (getTinymce() !== null) {
      initEditor();
    }
  });

  onActivated(() => {
    if (!mounting) initEditor();
  });

  onDeactivated(() => {
    if (!state.editor) return;
    state.editor.remove();
  });

  onBeforeUnmount(() => {
    if (!state.editor) return;
    state.editor.remove();
  });
</script>

<style lang="less" scoped></style>
